<!-- 二次定义的一个基础表格 -->
<template>
    <!-- v-bind 是总体的绑定,后面绑定的,如果有冲突会覆盖掉总体的绑定 -->
    <Form v-bind="getBindValue" ref="formElRef" :class="getFormClass" :model="formModel" @keypress.enter="handleEnterPress">
        <Row v-bind="getRow">
            <slot name="formHeader"></slot>

            <!-- 这个是空节点主要用于定义代码生成器等用途 -->
            <template v-for="schema in getSchemas" :key="schema.field">
                <FormItem
                    :tableAction="tableAction"
                    :formActions="formActions"
                    :schema="schema"
                    :formProps="getProps"
                    :allDefaultValues="defaultValueRef"
                    :setFormModel="setFormModel"
                    :formModel="formModel"
                >
                    <!-- 这个主要用于生成动态插槽 -->
                    <template #[item]="data" v-for="item in Object.keys($slots)">
                        <slot :name="item" v-bind="data || {}"></slot>
                    </template>
                </FormItem>
            </template>

            <!-- formAction提交重置等按钮 -->
            <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
                <template #[item]="data" v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']">
                    <slot :name="item" v-bind="data || {}"></slot>
                </template>
            </FormAction>
            <slot name="formFooter"></slot>
        </Row>
    </Form>
</template>

<script lang="ts">
import type { FormActions, FormProps, FormSchema } from './types/form';
import type { AdvanceState } from './types/hooks';
import type { Ref } from 'vue';

import {
    defineComponent,
    reactive,
    ref,
    computed,
    unref,
    onMounted,
    watch,
    nextTick,
} from 'vue';
import { Form, Row } from 'ant-design-vue';
import FormItem from './components/FormItem.vue';
import FormAction from './components/FormAction.vue';

import { dateItemType } from './helper';
import { dateUtil } from '/@/utils/dateUtil';

// import { cloneDeep } from 'lodash-es';
import { deepMerge } from '/@/utils';

import { useFormValues } from './hooks/useFormValues';
import useAdvanced from './hooks/useAdvanced';
import { useFormEvents } from './hooks/useFormEvents';
import { createFormContext } from './hooks/useFormContext';
import { useAutoFocus } from './hooks/useAutoFocus';
import { useModalContext } from '/@/components/Modal';

import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign';

/** 
 * 本组件的建议阅读:
 * onMounted ——函数挂在后注册组件
 * 
 */
export default defineComponent({
    name: 'BasicForm',
    components: { FormItem, Form, Row, FormAction },
    props: basicProps,
    // 本组件定义的事件,作为子组件被调用时候会触发一些事件的响应函数
    emits: ['advanced-change', 'reset', 'submit', 'register'],
    setup(props, { emit, attrs }) {
        const formModel = reactive<Recordable>({});
        const modalFn = useModalContext();
        // ？？这个不知道是搞毛的,还得进一步看看
        const advanceState = reactive<AdvanceState>({
            isAdvanced: true,
            hideAdvanceBtn: false,
            isLoad: false,
            actionSpan: 6,
        });

        const defaultValueRef = ref<Recordable>({});
        const isInitedDefaultRef = ref(false);


        /** ？？ 定义这个玩意有什么意义???这个一直都是是空的呀,等着被覆盖？ 
         * 注意Partial和Nullable的区别,前者是T中的属性optional,后者是对T的nullable 
         * 这个之所为用ref包着,是不是代表函数的返回值也是响应的？？
         * */
        const propsRef = ref<Partial<FormProps>>({});
        const schemaRef = ref<Nullable<FormSchema[]>>(null);        // log null
        const formElRef = ref<Nullable<FormActions>>(null);      // 这个名字起得奇奇怪怪的,应该是formAction？？

        const { prefixCls } = useDesign('basic-form');

        /** 计算属性,getProps对应表单的所有属性 */
        const getProps = computed((): FormProps => {
            return { ...props, ...unref(propsRef) } as FormProps;
        });

        /** 计算属性,从getProps里面getSchemas 并进行一定的预处理 */
        const getSchemas = computed((): FormSchema[] => {
            // tofix || 前面这个schemaRef没有看出有什么作用 */
            const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
            for (const schema of schemas) {
                const { defaultValue, component } = schema;
                // handle date type 如果component属于时间类型的组件
                if (defaultValue && dateItemType.includes(component)) {
                    if (!Array.isArray(defaultValue)) {
                        schema.defaultValue = dateUtil(defaultValue);
                    } else {
                        const def: moment.Moment[] = [];
                        defaultValue.forEach((item) => {
                            def.push(dateUtil(item));
                        });
                        schema.defaultValue = def;
                    }
                }
            }
            if (unref(getProps).showAdvancedButton) {
                return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
            } else {
                return schemas as FormSchema[];
            }
        });

        const getFormClass = computed(() => {
            return [
                prefixCls,
                {
                    [`${prefixCls}--compact`]: unref(getProps).compact,
                },
            ];
        });

        /** Get uniform row style and Row configuration for the entire form   */
        const getRow = computed((): Recordable => {
            const { baseRowStyle = {}, rowProps } = unref(getProps);
            return {
                style: baseRowStyle,
                ...rowProps,
            };
        });

        // 取得所有的绑定值
        const getBindValue = computed(
            () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
        );

        // 复杂表单的展收功能
        const { handleToggleAdvanced } = useAdvanced({
            advanceState,
            emit,
            getProps,
            getSchemas,
            formModel,
            defaultValueRef,
        });

        const { handleFormValues, initDefault } = useFormValues({
            getProps,
            defaultValueRef,
            getSchemas,
            formModel,
        });

        // 自动聚焦
        useAutoFocus({
            getSchemas,
            getProps,
            isInitedDefault: isInitedDefaultRef,
            formElRef: formElRef as Ref<FormActions>,
        });

        // tofix取得表单api函数,这个函数和下面的函数都太罗嗦了,争取合并精简一下
        const {
            handleSubmit,
            setFieldsValue,
            clearValidate,
            validate,
            validateFields,
            getFieldsValue,
            updateSchema,
            resetSchema,
            appendSchemaByField,
            removeSchemaByFiled,
            resetFields,
            scrollToField,
        } = useFormEvents({
            emit,
            getProps,
            formModel,
            getSchemas,
            defaultValueRef,
            formElRef: formElRef as Ref<FormActions>,
            schemaRef: schemaRef as Ref<FormSchema[]>,
            handleFormValues,
        });

        // ？？这个命名什么个意思真tm烦人,实际是给一些action赋值,关context什么事？
        createFormContext({
            resetAction: resetFields,
            submitAction: handleSubmit,
        });

        /** 监听getProps函数的数值 */
        watch(
            () => unref(getProps).model,
            () => {
                const { model } = unref(getProps);
                if (!model) return;
                setFieldsValue(model);
            },
            {
                immediate: true,
            },
        );

        // 有props数据变化就重新刷新schemas
        watch(
            () => unref(getProps).schemas,
            (schemas) => {
                resetSchema(schemas ?? []);
            },
        );

        watch(
            () => getSchemas.value,
            (schema) => {
                nextTick(() => {
                    //  Solve the problem of modal adaptive height calculation when the form is placed in the modal
                    modalFn?.redoModalHeight?.();
                });
                if (unref(isInitedDefaultRef)) {
                    return;
                }
                if (schema?.length) {
                    initDefault();
                    isInitedDefaultRef.value = true;
                }
            },
        );

        async function setProps(formProps: Partial<FormProps>): Promise<void> {
            propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
        }

        // 手动指定某个key的值
        function setFormModel(key: string, value: any) {
            formModel[key] = value;
            const { validateTrigger } = unref(getBindValue);
            if (!validateTrigger || validateTrigger === 'change') {
                validateFields([key]).catch((_) => { });
            }
        }

        // todo 所有的独立的键盘事件单独hook,找找有没有相关的库
        function handleEnterPress(e: KeyboardEvent) {
            const { autoSubmitOnEnter } = unref(getProps);
            if (!autoSubmitOnEnter) return;
            if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
                const target: HTMLElement = e.target as HTMLElement;
                if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
                    handleSubmit();
                }
            }
        }

        /**  可以理解为 formWithAction 是一个含有action的form实例,具体的actions在useFormEvent定义      */
        const formActions: Partial<FormActions> = {
            getFieldsValue,
            setFieldsValue,
            resetFields,
            updateSchema,
            resetSchema,
            setProps,
            removeSchemaByFiled,
            appendSchemaByField,
            clearValidate,
            validateFields,
            validate,
            submit: handleSubmit,
            scrollToField: scrollToField,
        };

        /** 挂载完毕会发送register注册事件并传递参数给register函数,の在useForm中  */
        onMounted(() => {
            initDefault();
            emit('register', formActions);
        });

        return {
            getBindValue,
            handleToggleAdvanced,
            handleEnterPress,
            formModel,
            defaultValueRef,
            advanceState,
            getRow,
            getProps,
            formElRef,
            getSchemas,
            formActions: formActions as any,
            setFormModel,
            getFormClass,
            getFormActionBindProps: computed(
                (): Recordable => ({ ...getProps.value, ...advanceState }),
            ),
            ...formActions,
        };
    },
});
</script>
<style lang="less">
@prefix-cls: ~"@{namespace}-basic-form";

.@{prefix-cls} {
    .ant-form-item {
        &-label label::after {
            margin: 0 6px 0 2px;
        }

        &-with-help {
            margin-bottom: 0;
        }

        &:not(.ant-form-item-with-help) {
            margin-bottom: 20px;
        }

        &.suffix-item {
            .ant-form-item-children {
                display: flex;
            }

            .ant-form-item-control {
                margin-top: 4px;
            }

            .suffix {
                display: inline-flex;
                padding-left: 6px;
                margin-top: 1px;
                line-height: 1;
                align-items: center;
            }
        }
    }

    .ant-form-explain {
        font-size: 14px;
    }

    &--compact {
        .ant-form-item {
            margin-bottom: 8px !important;
        }
    }
}
</style>
